--- modbus 从站
module(...,package.seeall)

local THISDEV,uid = 0x01, 2
---仿真回复阵列，实际数字位则用业务状态/数据替换
REG_T={}
REG_T[0x01]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 
REG_T[0x02]={} 
REG_T[0x03]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 
REG_T[0x04]={} 

--[[
--   起始        地址    功能代码    数据       CRC校验    结束
-- 3.5 字符     8 位      8 位      N x 8 位   16 位      3.5 字符
--- 发送modbus数据函数
@function   modbus_resp
@param      slaveaddr : 从站地址
            Instructions:功能码
            hexdat 回复的数据 HEX STRING
@return     无
@usage modbus_resp("0x01","0x01","0x0101","0x04")
]]
local function modbus_resp(slaveaddr,Instructions,hexdat)
    local data = (string.format("%02x",slaveaddr)..string.format("%02x",Instructions)..hexdat):fromHex()
    local modbus_crc_data= pack.pack('<h', crypto.crc16("MODBUS",data))
    local data_tx = data..modbus_crc_data
    sys.publish("UART_SENT_RDY_"..uid , uid, data_tx)
end
local function MSK_DIGI(pos)
    local msk =0
    for i=1,pos do
        msk=bit.lshift(msk,1)
        msk=msk+1
    end
    return msk
end
function modbus_read(cacheData)
    if cacheData and cacheData:len()>0 then
        local a = string.toHex(cacheData)
        --log.info("modbus接收数据:",a)
        -- 0x01: 读线圈寄存器               位操作     单个或多个
        -- 0x02: 读离散输入寄存器           位操作     单个或多个
        -- 0x03: 读保持寄存器               字节操作   单个或多个
        -- 0x04: 读输入寄存器               字节操作   单个或多个
        -- 0x05: 写单个线圈寄存器           位操作     单个
        -- 0x06: 写单个保持寄存器           字节操作   单个
        -- 0x0f: 写多个线圈寄存器           位操作     多个
        -- 0x10: 写多个保持寄存器           字节操作   多个
        local nextpos ,dev, func = pack.unpack(cacheData,"bb",1)
        log.info("nextpos ,dev, func ",nextpos,string.format("%02X,%02X", dev or 0, func or 0))
        --01 06 0001 0002 59CB
        if dev == THISDEV then
            if func == 0x01 or func == 0x02 or func == 0x03 or func == 0x04 or func == 0x05 or func == 0x06 then
                if #cacheData >= 8 then
                    local strcrc = pack.pack('<h', crypto.crc16("MODBUS", cacheData:sub(1, 6)))
                    if strcrc == cacheData:sub(7, 8) then
                        local _, reg, val = pack.unpack(cacheData, ">H>H", nextpos)
                        --log.info("a-func,crc is correct!", func, string.format("reg=0x%04X, val=0x%04X", reg, val))
                        --校验正确后，根据不同的功能码做回复(DEMO 忽略起始地址)
                        if func == 0x01 or func == 0x02 then
                            ---01 01 00 00 00 0A BC 0D
                            local _, bitcnt = pack.unpack(cacheData, ">H", 5)
                            if bitcnt > 0 then
                                local bytlen, hflg = 0, false
                                if bitcnt % 8 == 0 then
                                    bytlen = bitcnt / 8
                                else
                                    bytlen = (bitcnt - bitcnt % 8) / 8 + 1
                                    hflg = true
                                end
                                if bytlen <= #REG_T[func] then
                                    local strhex = ""
                                    for i = 1, bytlen do
                                        if hflg and i == bytlen then
                                            --不足一个字节的位数，主机和从机之间定好协议，这里就简化成低位掩码,用户自己对照
                                            local msk = MSK_DIGI(bitcnt % 8)
                                            strhex = strhex .. string.format("%02x", bit.band(REG_T[func][i], msk))
                                        else
                                            strhex = strhex .. string.format("%02x", REG_T[func][i])
                                        end
                                    end
                                    --log.info(" bytlen , #REG_T[func], msk,strhex", bytlen, #REG_T[func], msk, strhex)
                                    modbus_resp(THISDEV, func, string.format("%02x%s", #strhex / 2, strhex))
                                else
                                    modbus_resp(THISDEV, func + 0x80, string.format("%02x", 0x02))
                                end
                            end
                        elseif func == 0x03 or func == 0x04 then
                            local _, bytlen = pack.unpack(cacheData, ">H", 5)
                            if bytlen <= #REG_T[func] then
                                local strhex = ""
                                for i = 1, bytlen do
                                    strhex = strhex .. string.format("%04x", REG_T[func][i])
                                end
                                --log.info(" bytlen , #REG_T[func], msk,strhex", bytlen, #REG_T[func], msk, strhex)
                                modbus_resp(THISDEV, func, string.format("%02x%s", #strhex / 2, strhex))
                            else
                                modbus_resp(THISDEV, func + 0x80, string.format("%02x", 0x02))
                            end
                        elseif func == 0x05  then
                            log.info('继电器指令：',string.format("func=0x%04X,reg=0x%04X, val=0x%04X", func,reg, val))
    
                            if reg==1  then
                                if val==0xff00 then
                                    power.openSW1()
                                    log.info('打开继电器1')
                                elseif val==0 then
                                    power.closeSW1()
                                    log.info('关闭继电器1')
                                 end
                            elseif reg==2 then
                                if val==0xff00 then
                                    log.info('打开继电器2')
                                    power.openSW2()
                                 elseif val==0 then
                                    power.closeSW2()
                                    log.info('关闭继电器2')
                                 end
                            elseif reg==3 then
                                if val==0xff00 then
                                    log.info('打开继电器3')
                                    power.openSW3()
                                 elseif val==0 then
                                    power.closeSW3()
                                    log.info('关闭继电器3')
                                 end
                            elseif reg==4 then
                                if val==0xff00 then
                                    log.info('打开继电器4')
                                    power.openSW4()
                                 elseif val==0 then
                                    power.closeSW4()
                                    log.info('关闭继电器4')
                                 end
                            end
                            --log.info("save set data ", func, string.format("reg=0x%04X, val=0x%04X", reg, val))
                            local strhex = string.format("%04X%04X", reg, val)
                            --log.info("继电器状态：", power.getSW3(), power.getSW2(), power.getSW1())
                            modbus_resp(THISDEV, func, strhex)
                        elseif func == 0x06 then
                            log.info("unkonw func", func)
                            --modbus_resp(THISDEV, func + 0x80, string.format("%02x", 0x01))
                        end
                    else
                        log.info("a-func #cacheData  crc, calcrc", func, #cacheData, cacheData:sub(7, 8):toHex(),
                            strcrc:toHex())
                    end
                end
            elseif func == 0x0F then
                local dlen = cacheData:byte(nextpos + 4)
                if #cacheData >= 7 + dlen + 2 then
                    local strcrc = pack.pack('<h', crypto.crc16("MODBUS", cacheData:sub(1, 7 + dlen)))
                    if strcrc == cacheData:sub(7 + dlen + 1, 7 + dlen + 2) then
                        local _, reg, val = pack.unpack(cacheData, ">H>H", nextpos)
                        local tmpdat = cacheData:sub(8, dlen + 8 - 1)
                        --假设写入都是成功的，实际上写入也要判断值域：回复为起始地址和线圈个数
                        log.info("b-func crc is correct!", func, dlen, "will save:", tmpdat:toHex())
                        local strhex = string.format("%04X%04X", reg, val)
                        modbus_resp(THISDEV, func, strhex)
                    else
                        log.info("b-func,#cacheData  crc, calcrc", func, #cacheData,
                            cacheData:sub(7 + dlen + 1, 7 + dlen + 2):toHex(), strcrc:toHex())
                    end
                end
            elseif func == 0x10 then
                local dlen = cacheData:byte(nextpos + 4)
                log.info("#cacheData,func,dlen=", #cacheData, func, dlen)
                --01 10 0000 000A 14 0000000000000000000000000000000000000000 70FE
                if #cacheData >= 7 + dlen + 2 then
                    local strcrc = pack.pack('<h', crypto.crc16("MODBUS", cacheData:sub(1, 7 + dlen)))
                    if strcrc == cacheData:sub(7 + dlen + 1, 7 + dlen + 2) then
                        local _, reg, val = pack.unpack(cacheData, ">H>H", nextpos)
                        local tmpdat = cacheData:sub(8, dlen + 8 - 1)
                        --假设写入都是成功的，实际上写入也要判断值域：回复为起始地址和字节个数
                        log.info("c-func crc is correct!", func, dlen, "will save:", tmpdat:toHex(), #tmpdat / 2, "words")
                        local strhex = string.format("%04X%04X", reg, val)
                        modbus_resp(THISDEV, func, strhex)
                    else
                        log.info("c-func,#cacheData  crc, calcrc", func, #cacheData,
                            cacheData:sub(7 + dlen + 1, 7 + dlen + 2):toHex(), strcrc:toHex())
                    end
                end
            end
        end
    end
end
